﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace ProtoGame.GameLib.GameComponents
{
    //public delegate void ItemActionHandler(String itemText);


    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Menu : Microsoft.Xna.Framework.DrawableGameComponent
    {
        #region Tipos Internos

        public struct EntradasMenu
        {
            public bool Baixo { get; set; }
            public bool Cima { get; set; }
            public bool Confirma { get; set; }
        }

        public class MenuEventArgs : EventArgs
        {
            public String SelectedOption { get; private set; }

            public MenuEventArgs(String selectedOption)
            {
                this.SelectedOption = selectedOption;
            }
        }

        private enum MenuStates
        {
            Starting,
            Active,
            Ending,
            Inactive
        }

        #endregion

        #region Membros

        private TimeSpan changeSpan;
        private float changeProgress;

        private String menuTitle;
        private List<String> optionsList;
        private MenuStates menuState;
        private int selectedOption;

        // FIX: Por enquanto tudo vai funcionar por esse event geral, mas o ideal seria cada item do menu ter seu próprio event.
        public event EventHandler<MenuEventArgs> MenuAction;

        private SpriteFont menuFont;
        private String menuFontPath;
        private SpriteBatch spriteBatch;

        #endregion

        #region Propriedades

        protected EntradasMenu Entradas;

        #endregion

        public Menu(String menuTitle, String menuFontPath, Game game)
            : base(game)
        {
            optionsList = new List<String>();
            changeSpan = TimeSpan.FromMilliseconds(800);
            selectedOption = 0;
            changeProgress = 0;
            menuState = MenuStates.Inactive;

            this.menuFontPath = menuFontPath;
            this.menuTitle = menuTitle;
        }

        public void AddMenuOption(String itemText)
        {
            optionsList.Add(itemText);
        }

        public void WakeUp()
        {
            menuState = MenuStates.Starting;
        }

        private void ProcessInput()
        {
            if (menuState == MenuStates.Active)
            {
                if (Entradas.Baixo)
                    selectedOption++;

                if (Entradas.Cima)
                    selectedOption--;

                if (selectedOption < 0)
                    selectedOption = optionsList.Count - 1;

                if (selectedOption >= optionsList.Count)
                    selectedOption = 0;

                if (Entradas.Confirma)
                    menuState = MenuStates.Ending;
            }
        }

        private void UpdateInput()
        {
            IBasicInput entradas = Game.Services.GetService(typeof(IBasicInput)) as IBasicInput;
            Entradas.Baixo = entradas.Down();
            Entradas.Cima = entradas.Up();
            Entradas.Confirma = entradas.Confirm();
        }

        protected void OnMenuAction()
        {
            if (MenuAction != null)
                MenuAction(this, new MenuEventArgs(optionsList[selectedOption]));
        }

        #region Overrides DrawableGameComponent

        public override void Initialize()
        {
            spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            menuFont = Game.Content.Load<SpriteFont>(menuFontPath);

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            UpdateInput();

            if ((menuState == MenuStates.Starting) || (menuState == MenuStates.Ending))
                changeProgress += (float)(gameTime.ElapsedGameTime.TotalMilliseconds / changeSpan.TotalMilliseconds);

            if (menuState == MenuStates.Active)
                ProcessInput();

            if (changeProgress >= 1.0f)
            {
                changeProgress = 0.0f;
                if (menuState == MenuStates.Starting)
                    menuState = MenuStates.Active;
                else if (menuState == MenuStates.Ending)
                {
                    menuState = MenuStates.Inactive;
                    OnMenuAction();
                }
            }

            base.Update(gameTime);
        }

        // FIX: Passar os máximo de cálculos possível para o Update()
        public override void Draw(GameTime gameTime)
        {
            if (menuState != MenuStates.Inactive)
            {
                float smoothedProgress = MathHelper.SmoothStep(0, 1, changeProgress);

                int verPosition = 300;
                float horPosition = 300;
                float alphaValue;

                switch (menuState)
                {
                    case MenuStates.Starting:
                        horPosition -= 200 * (1.0f - smoothedProgress);
                        alphaValue = smoothedProgress;
                        break;

                    case MenuStates.Ending:
                        horPosition += 200 * smoothedProgress;
                        alphaValue = 1.0f - smoothedProgress;
                        break;

                    default:
                        alphaValue = 1;
                        break;
                }

                spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Texture, SaveStateMode.SaveState);

                Color titleColor = new Color(new Vector4(1, 1, 1, alphaValue));
                spriteBatch.DrawString(
                    menuFont,
                    menuTitle,
                    new Vector2(horPosition, 225),
                    titleColor,
                    0,
                    Vector2.Zero,
                    1.5f,
                    SpriteEffects.None,
                    0);

                foreach(String option in optionsList)
                {
                    Vector2 itemPostition = new Vector2(horPosition, verPosition);
                    Color itemColor = Color.White;

                    if (option == optionsList[selectedOption])
                        itemColor = new Color(new Vector4(1, 0, 0, alphaValue));
                    else
                        itemColor = new Color(new Vector4(1, 1, 1, alphaValue));

                    spriteBatch.DrawString(
                        menuFont,
                        option,
                        itemPostition,
                        itemColor,
                        0,
                        Vector2.Zero,
                        1,
                        SpriteEffects.None,
                        0);
                    verPosition += 30;
                }

                spriteBatch.End();
            }

            base.Draw(gameTime);
        }

        #endregion

    }
}